package com.google.code.xconf.schema;

import com.google.code.xconf.Configuration;
import com.google.code.xconf.ConfigurationParser;
import com.google.code.xconf.ConfigurationWriter;
import com.google.code.xconf.PropertyGroup;
import com.google.code.xconf.schema.definition.PropertiesDefinition;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import static com.google.code.xconf.schema.SchemaConstants.*;

public class Schema extends SchemaDefinition {

    private PropertiesDefinition propertiesDefinition;

    public Schema(Configuration configuration) {
        super(null, configuration, new SchemaSettings());
        configure();
    }

    private void configure() {
        settings.setName(resolveDefinitionPropertyString(SCHEMA_NAME, false));
        settings.setVersion(resolveDefinitionPropertyString(SCHEMA_VERSION, false));
        settings.setAuthor(resolveDefinitionPropertyString(SCHEMA_AUTHOR, false));
        settings.setLocation(resolveDefinitionPropertyString(SCHEMA_LOCATION, true));
        settings.setPropertyIdentifierCaseSensitive(resolveDefinitionPropertyBoolean(PROPERTY_IDENTIFIER_CASE_SENSITIVE, true, true));
        settings.setPropertyValueCaseSensitive(resolveDefinitionPropertyBoolean(PROPERTY_VALUE_CASE_SENSITIVE, true, true));
        settings.setPropertyOptional(resolveDefinitionPropertyBoolean(PROPERTY_OPTIONAL, true, true));
        settings.setPropertiesStrictOrdering(resolveDefinitionPropertyBoolean(PROPERTIES_STRICT_ORDERING, true, false));
        settings.setSameNamedPropertiesAllowed(resolveDefinitionPropertyBoolean(SAME_NAMED_PROPERTIES_ALLOWED, true, false));
        settings.setSameNamedGroupsAllowed(resolveDefinitionPropertyBoolean(SAME_NAMED_GROUPS_ALLOWED, true, true));
        propertiesDefinition = resolvePropertiesDefinition();
    }

    @Override
    protected List<String> getValidNodeNames() {
        return Arrays.asList(SCHEMA_NAME, SCHEMA_VERSION, SCHEMA_AUTHOR, SCHEMA_LOCATION,
                PROPERTY_IDENTIFIER_CASE_SENSITIVE, PROPERTY_VALUE_CASE_SENSITIVE, PROPERTY_OPTIONAL,
                PROPERTIES_STRICT_ORDERING, SAME_NAMED_PROPERTIES_ALLOWED, SAME_NAMED_GROUPS_ALLOWED,
                PROPERTIES_DEFINITION);
    }

    public void validate(Configuration configuration) throws SchemaValidationException {
        propertiesDefinition.validate(configuration.getNodes());
    }

    public PropertiesDefinition resolvePropertiesDefinition() {
        Collection<PropertyGroup> groups = definition.getPropertyGroups(PROPERTIES_DEFINITION);
        if (!groups.isEmpty()) {
            if (groups.size() > 1) {
                throw new SchemaLayoutException("Only one " + PROPERTIES_DEFINITION + " specification can be declared");
            }
            return new PropertiesDefinition(definition.getPropertyGroups(PROPERTIES_DEFINITION).iterator().next(), settings);
        }
        throw new SchemaLayoutException("The " + PROPERTIES_DEFINITION + " specification is missing");
    }

    public static void main(String[] args) {

        try {
            ConfigurationParser parser = new ConfigurationParser();
            Configuration schemaConf = parser.parse(new FileInputStream("/Users/golanhall/Projects/xconf/docs/examples/ExampleValidationSchema.conf"));
            Configuration conf = parser.parse(new FileInputStream("/Users/golanhall/Projects/xconf/docs/examples/ValidateMe.conf"));
            System.out.println(new ConfigurationWriter().toString(conf));

            Schema schema = new Schema(schemaConf);
            schema.validate(conf);

        } catch (ParseException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        catch (SchemaValidationException e) {
            e.printStackTrace();
        }

    }

}
